 
/* 
 * Copyright (C) Ignacio Lucas
 */
 
/*
 *  Utilidades para la construccion de la aplicacion bancaria de prueba.
 *
 *  Ignacio Lucas <ignacio.lucas@gmail.com>
 *
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include "accounts_service.h"

static const char *TIPOS_CUENTA[]={"CA $", "CC $", "CC U$S"};
static const char *MONEDA_POR_TIPO_CUENTA[]={"$", "$", "U$S"};


static int sort_by_fecha(const void *m1, const void *m2);
static int sort_by_monto(const void *m1, const void *m2);

// busqueda del siguiente token de un string, separado cada token por el caracter "separator".
static void next_token(char *buffer, size_t *len, char separator)
{
	char *ptr_next_token=NULL;
	
	*len=0;
	ptr_next_token=strchr(buffer, separator);
	if (ptr_next_token)
	{
		*len=ptr_next_token - buffer;
	}
}

/** Del string "all_movimientos" se extraen los bytes de largo len para construir un "movimiento_cuenta".**/
static void assign_movimiento(movimiento_cuenta *movimiento, char *all_movimiento, size_t len)
{
	char temp_monto[50];
	size_t token_len;

	movimiento->debito_credito=all_movimiento[0];
	memset(movimiento->fecha_y_hora, 0, sizeof(movimiento->fecha_y_hora));
	strncpy(movimiento->fecha_y_hora, all_movimiento+2, 19);
	
	memset(temp_monto, 0, sizeof(temp_monto));

	next_token(all_movimiento + 22, &token_len, ',');
	strncpy(temp_monto, all_movimiento + 22, token_len);
	movimiento->monto=atol(temp_monto);

	memset(movimiento->descripcion, 0, sizeof(movimiento->descripcion));
	strncpy(movimiento->descripcion, all_movimiento + 22 + token_len + 1, len - (22 + token_len + 1));
}

/** Del string "all_cuenta" se extraen los bytes de largo len para construir una "cuenta" sin sus movimientos.**/
static void assign_cuenta(cuenta *cuenta, char *buffer, int len)
{
	size_t token_len;
	char temp_saldo[50];
	int acum=0;

	// parseo el numero de cuenta, el tipo de cuenta y el saldo.
	next_token(buffer, &token_len, ',');
	strncpy(cuenta->cuenta, buffer, token_len);
	buffer+=(token_len+1);
	acum+=(token_len+1);

	// extraemos tipo de cuenta
	next_token(buffer, &token_len, ',');
	cuenta->tipo_cuenta=buffer[0];
	buffer+=(token_len+1);
	acum+=(token_len+1);

	memset(temp_saldo, 0, sizeof(temp_saldo));
	strncpy(temp_saldo, buffer, len - acum);
	cuenta->saldo=atol(temp_saldo);
	return;
}

/** 
Estructura que responde el servidor para cuentas de cliente.

N;[X,T,S;]*

N=cantidad de cuentas
X=numero de cuenta
T=tipo de cuenta(0=caja de ahorro pesos, 1=cuenta corriente pesos).
S=saldo
**/
cuenta *get_cuentas(char *buffer, int *count)
{
	cuenta *cuentas=NULL;
	char str_cant_cuentas[20];
	size_t len;
	int i;

	*count=0;
	memset(str_cant_cuentas, 0, sizeof(str_cant_cuentas));
	// ubicamos la cantidad de cuentas y en base a eso construimos el vector de cuentas.

	next_token(buffer, &len, ';');
	strncpy(str_cant_cuentas, buffer, len);
	buffer+=(len+1);
	*count=atoi(str_cant_cuentas);

	if (*count > 0)
	{
		cuentas = (cuenta *)malloc((*count)*sizeof(cuenta));
		if (cuentas == (cuenta *)NULL)
		{
			return NULL;
		}
		memset(cuentas, 0, (*count)*sizeof(cuenta));

		// parseo cada cuenta.
		for (i=0; i<(*count); i++)
		{
				next_token(buffer, &len, ';');
				assign_cuenta(&(cuentas[i]), buffer, len);
				buffer+=(len+1);
		}
	}
	return cuentas;
}

/** 
Estructura que responde el servidor para movimientos de cuenta.

X;T;S;N;[movimiento;]?[movimiento;]*

X=numero de cuenta
T=tipo de cuenta(0=caja de ahorro pesos, 1=cuenta corriente pesos).
S=saldo
N=cantidad de movimientos

donde movimiento=debito_credito,fecha_y_hora,monto,descripcion
debito_credito=0(para débito), 1(para crédito)
fecha_y_hora:dd/mm/yyyy hh:mi:ss
monto:monto en centavos del movimiento.

ordenamiento: sort_fecha_o_monto = 0 --> ordena por fecha, cualquier otro valor ordena por monto.
**/
cuenta *get_movimientos_de_cuenta(char *buffer, int sort_fecha_o_monto)
{
	size_t len=0;
	cuenta *c=NULL;
	char str_cant_movimientos[20];
	char temp_saldo[50];

	int i=0;

	c=(cuenta *)malloc(sizeof(cuenta));
	if (c == NULL)
	{
		return NULL;
	}
	memset(c, 0, sizeof(cuenta));

	// parseo el numero de cuenta, el tipo de cuenta, el saldo y la cantidad de movimientos.
	next_token(buffer, &len, ';');
	strncpy(c->cuenta, buffer, len);

	buffer+=(len+1);
	// extraemos tipo de cuenta
	next_token(buffer, &len, ';');
	c->tipo_cuenta=buffer[0];
	buffer+=(len+1);

	// extraemos saldo.
	memset(temp_saldo, 0, sizeof(temp_saldo));
	next_token(buffer, &len, ';');
	strncpy(temp_saldo, buffer, len);
	c->saldo=atol(temp_saldo);
	buffer+=(len+1);

	// extraemos cantidad de movimientos.
	next_token(buffer, &len, ';');
	memset(str_cant_movimientos, 0, sizeof(str_cant_movimientos));
	strncpy(str_cant_movimientos, buffer, len);
	buffer+=(len+1);
	c->movimientos.len=atoi(str_cant_movimientos);
	
	if (c->movimientos.len > 0)
	{
		c->movimientos.movimiento = (movimiento_cuenta *)malloc(c->movimientos.len*sizeof(movimiento_cuenta));
		if (c->movimientos.movimiento == NULL)
		{
			free(c);
			return NULL;
		}
		for (i=0; i<c->movimientos.len; i++)
		{
				next_token(buffer, &len, ';');
				assign_movimiento(&(c->movimientos.movimiento[i]), buffer, len);
				buffer+=(len+1);
		}
	}
	// aplicamos el ordenamiento.
	if (sort_fecha_o_monto == 0)
	{
		qsort((c->movimientos.movimiento), c->movimientos.len, sizeof(movimiento_cuenta), sort_by_fecha);
	}
	else
	{
		qsort((c->movimientos.movimiento), c->movimientos.len, sizeof(movimiento_cuenta), sort_by_monto);
	}
	return c;
}

// suma de todos los debitos y creditos de los movimientos de una cuenta.
void totalizar_monto(movimiento_cuenta *movimientos, int len, long *debitos, long *creditos)
{
	int i=0;
	*debitos=*creditos=0L;

	for (i=0; i<len; i++)
	{
		if (movimientos[i].debito_credito=='0')
		{
			*debitos+=movimientos[i].monto;
		}
		else
		{
			*creditos+=movimientos[i].monto;
		}
	}
}

// totales de debitos y creditos de una cuenta.
void totalizar_cuenta(cuenta *cuenta, long *debitos, long *creditos)
{
	*debitos=*creditos=0;
	if (cuenta->movimientos.len > 0)
	{
		totalizar_monto(cuenta->movimientos.movimiento, cuenta->movimientos.len, debitos, creditos);
	}
}

// liberamos espacio de memoria del vector de cuentas, de tamanio "count".
int cuentas_destroy(cuenta *cuentas, int count)
{
	int i=0;
	for (i=0; i<count; i++)
	{
		if (cuentas[i].movimientos.movimiento != NULL)
		{
			free(cuentas[i].movimientos.movimiento);
		}
	}
	free(cuentas);
	return 0;
}

// totaliza los movimientos de cuenta de un vector de movimientos de cuentas.
void totalizar_movimientos(movimientos_cuenta *movimientos, long *debitos, long *creditos)
{
	int i=0;
	*debitos=*creditos=0L;

	for (i=0; i<movimientos->len; i++)
	{
		if (movimientos->movimiento[i].debito_credito==0)
		{
			*debitos+=movimientos->movimiento[i].monto;
		}
		else
		{
			*creditos+=movimientos->movimiento[i].monto;
		}
	}
}

// ordena dos objetos que repesentan un movimiento de cuenta por mayor monto.
static int sort_by_monto(const void *m1, const void *m2)
{
	movimiento_cuenta *mov1, *mov2;
	long monto1, monto2;

	mov1 = (movimiento_cuenta *)m1;
	mov2 = (movimiento_cuenta *)m2;

	monto1=mov1->monto;
	monto2=mov2->monto;

	if (monto1 == monto2) return 0;
	return monto1 < monto2 ? 1:-1;
}

// convierte una fecha (yymmdd o hhmiss) de formato string a un long.
static long fecha_to_number(char *value, int fecha_hora)
{
	char str_valor[8]; // guardare yyyymmdd ó hhmiss

	memset(str_valor, 0, sizeof(str_valor));
	if (fecha_hora == 0)
	{
		str_valor[0]=value[6];
		str_valor[1]=value[7];
		str_valor[2]=value[8];
		str_valor[3]=value[9];
		str_valor[4]=value[3];
		str_valor[5]=value[4];
		str_valor[6]=value[0];
		str_valor[7]=value[1];
	}
	else
	{
		str_valor[0]=value[11];
		str_valor[1]=value[12];
		str_valor[2]=value[14];
		str_valor[3]=value[15];
		str_valor[4]=value[17];
		str_valor[5]=value[18];
	}
	return atol(str_valor);	
}

// ordenado por fecha. Las fechas son de la forma: dd/mm/yyyy hh24:mi:ss
// entonces, convierto al numero yyyymmdd y comparo fechas. Si son iguales, comparo horas.
// ej: 23/05/2010 --> 20100523, luego 23:12:05 --> 231205.
static int sort_by_fecha(const void *m1, const void *m2)
{
	movimiento_cuenta *mov1, *mov2;
	long valor1, valor2;

	mov1 = (movimiento_cuenta *)m1;
	mov2 = (movimiento_cuenta *)m2;

	valor1=fecha_to_number(mov1->fecha_y_hora,0);
	valor2=fecha_to_number(mov2->fecha_y_hora,0);

	if (valor1 == valor2)
	{
		valor1=fecha_to_number(mov1->fecha_y_hora,1);
		valor2=fecha_to_number(mov2->fecha_y_hora,1);
		if (valor1 == valor2)
		{
			return 0;
		}
		return valor1 < valor2 ? 1:-1;
	}
	else
	{
		return valor1 < valor2 ? 1:-1;
	}
}

// devuelve la descripcion de un tipo de cuenta a partir de su codigo.
const char *get_tipo_cuenta(char tipo_cuenta)
{
	return TIPOS_CUENTA[(int)(tipo_cuenta - '0')];
}

// devuelve la descripcion de moneda a partir del codigo de tipo de cuenta.
const char *get_moneda_por_tipo_cuenta(char tipo_cuenta)
{
	return MONEDA_POR_TIPO_CUENTA[(int)(tipo_cuenta - '0')];
}


int main2(int argc, char **argv)
{
	/*movimiento_cuenta *movimientos;

	long debitos, creditos;
	cuenta *c;*/
	int i=0;
	cuenta *all_cuentas;
	int count_cuentas;

/*
	movimientos = (movimiento_cuenta *)malloc(4*sizeof(movimiento_cuenta));

	movimientos[0].monto=1250;
	movimientos[1].monto=2550;
	movimientos[2].monto=4100;
	movimientos[3].monto=400;

	strcpy(movimientos[0].fecha_y_hora,"01/03/2010 12:23:00");
	strcpy(movimientos[1].fecha_y_hora,"05/07/2010 15:56:21");
	strcpy(movimientos[2].fecha_y_hora,"01/03/2010 12:22:59");
	strcpy(movimientos[3].fecha_y_hora,"01/11/2008 22:15:00");

	movimientos[0].debito_credito=0;
	movimientos[1].debito_credito=1;
	movimientos[2].debito_credito=1;
	movimientos[3].debito_credito=1;


	for (i=0; i<4; i++)
	{
		printf("POINTER: %p, ", &movimientos[i]);
		printf("monto: %d\n", movimientos[i].monto);
	} 

	qsort(movimientos, 4, sizeof(movimiento_cuenta), sort_by_fecha);

	totalizar_monto(movimientos, 4, &debitos, &creditos);
	printf("\nDEBITOS: %d, CREDITOS: %d\n", debitos, creditos);
	printf("\n");

	for (i=0; i<4; i++)
	{
		printf("POINTER: %p, ", &movimientos[i]);
		printf("monto: %d\n", movimientos[i].monto);
	} 
	free(movimientos);

	c=get_movimientos_de_cuenta("1234;1;1549;2;1,05/07/2010 00:31:00,1500,EXTRACCION;0,05/07/2010 00:31:50,950,COMPRA SHELL;",0);

	printf("\nsaldo de cuenta: %ld", c->saldo);
	for (i=0; i<c->movimientos.len; i++)
	{
		printf("\nmov[%d]. tipo:%c, fecha: %s, monto: %d, descripcion: %s\n", i, c->movimientos.movimiento[i].debito_credito, c->movimientos.movimiento[i].fecha_y_hora, c->movimientos.movimiento[i].monto, c->movimientos.movimiento[i].descripcion); fflush(stdout);
	}
	free(c);
*/
	printf("\n\n\n");
	all_cuentas=get_cuentas("3;1234,1,1500;5678,0,2000;9012,2,9000;", &count_cuentas);
	for (i=0; i<count_cuentas; i++)
	{
		printf("\ncuenta: _%s_, tipo: _%c_, saldo, _%ld_", all_cuentas[i].cuenta, all_cuentas[i].tipo_cuenta, all_cuentas[i].saldo);
	}
	printf("\n\n\n");
	printf("\n\%d\n", count_cuentas);
	free(all_cuentas);
	return 0;

}

int main3(int argc, char **argv)
{
	int count_cuentas=0;
	cuenta *all_cuentas=NULL;
	int i=0;

	printf("\n\n\n");
	all_cuentas=get_cuentas("3;1234,1,1500;5678,0,2000;9012,2,9000;", &count_cuentas);
	for (i=0; i<count_cuentas; i++)
	{
		printf("\ncuenta: _%s_, tipo: _%c_, saldo, _%ld_", all_cuentas[i].cuenta, all_cuentas[i].tipo_cuenta, all_cuentas[i].saldo);
	}
	printf("\n\n\n");
	printf("\n\%d\n", count_cuentas);
	free(all_cuentas);
	return 0;
}
